/*
 *    Copyright © OpenAtom Foundation.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.inspur.edp.bef.engine.repository.util;

import com.inspur.edp.bef.bizentity.GspBizEntityObject;
import com.inspur.edp.bef.bizentity.GspBusinessEntity;
import com.inspur.edp.bef.bizentity.common.BefDtBeanUtil;
import com.inspur.edp.bef.engine.entity.AssociationInfo;
import com.inspur.edp.cef.api.repository.INestedRepository;
import com.inspur.edp.cef.api.repository.readerWriter.ICefReader;
import com.inspur.edp.cef.designtime.api.IGspCommonField;
import com.inspur.edp.cef.designtime.api.element.EnumIndexType;
import com.inspur.edp.cef.designtime.api.element.GspAssociation;
import com.inspur.edp.cef.designtime.api.element.GspElementObjectType;
import com.inspur.edp.cef.designtime.api.element.GspEnumValue;
import com.inspur.edp.cef.entity.changeset.ValueObjModifyChangeDetail;
import com.inspur.edp.cef.entity.entity.EntityDataPropertyValueUtils;
import com.inspur.edp.cef.entity.entity.ICefData;
import com.inspur.edp.cef.entity.entity.IEntityData;
import com.inspur.edp.cef.repository.adaptor.EntityRelationalAdaptor;
import com.inspur.edp.cef.repository.exception.CefRepositoryException;
import com.inspur.edp.cef.repository.readerwriter.CefMappingReader;
import com.inspur.edp.commonmodel.engine.api.common.CMEngineUtil;
import com.inspur.edp.das.commonmodel.IGspCommonElement;
import com.inspur.edp.lcm.metadata.api.entity.GspMetadata;
import com.inspur.edp.udt.api.UdtManagerUtils;
import com.inspur.edp.udt.designtime.api.entity.ComplexDataTypeDef;
import com.inspur.edp.udt.designtime.api.entity.SimpleDataTypeDef;
import com.inspur.edp.udt.designtime.api.entity.UnifiedDataTypeDef;
import com.inspur.edp.udt.designtime.api.entity.dbInfo.ColumnMapType;
import io.iec.edp.caf.commons.utils.SpringBeanUtils;
import lombok.var;
import org.eclipse.core.internal.utils.Convert;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.sql.Clob;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;

public class ReadUtils {
    protected static Logger logger = LoggerFactory.getLogger(ReadUtils.class);
    //region 非udt字段赋值
    public static Object getValue(IGspCommonElement element, ICefReader reader) {
        if (element.getObjectType() == GspElementObjectType.Association)
            return getAssoValue(element, reader);
        Object value = reader.readValue(element.getLabelID());
        if (element.getObjectType() == GspElementObjectType.Enum)
            return getEnumValue(element, value);
        switch (element.getMDataType()) {
            case String:
                return getStringValue(element, value);
            case Integer:
                return getIntValue(element, value);
            case Decimal:
                return getDecimalValue(element, value);
            case Boolean:
                return getBoolValue(element, value);
            case Date:
            case DateTime:
                return getDateValue(element, value);
            case Text:
                return getTextValue(element, value);
            case Binary:
                return getBinaryValue(element, value);
        }

        throw new CefRepositoryException("字段[" + element.getLabelID() + "]没有合适的读取方法,数据类型:" + element.getMDataType());
    }

    public static Object getValue(String prefix, IGspCommonElement element, ICefReader reader) {
        if (element.getObjectType() == GspElementObjectType.Association)
            return getAssoValue(element, reader);
        Object value = reader.readValue(prefix +"_" + element.getCode());
        if (element.getObjectType() == GspElementObjectType.Enum)
            return getEnumValue(element, value);
        switch (element.getMDataType()) {
            case String:
                return getStringValue(element, value);
            case Integer:
                return getIntValue(element, value);
            case Decimal:
                return getDecimalValue(element, value);
            case Boolean:
                return getBoolValue(element, value);
            case Date:
            case DateTime:
                return getDateValue(element, value);
            case Text:
                return getTextValue(element, value);
            case Binary:
                return getBinaryValue(element, value);
        }

        throw new CefRepositoryException("字段" + element.getLabelID() + "没有合适的读取方法" + element.getMDataType());
    }

    private static Object getEnumValue(IGspCommonElement element, Object value) {
        if (element.getContainEnumValues() == null)
            throw new CefRepositoryException("字段" + element.getLabelID() + "设置为枚举类型，但未添加枚举项");
        GspEnumValue defaultEnumValue = null;
        HashMap<String, GspEnumValue> stringIndexEnumValue = new HashMap<>();
        HashMap<Integer, GspEnumValue> intIndexEnumValue = new HashMap<>();
        for (GspEnumValue enumValue : element.getContainEnumValues()) {
            if (enumValue.getIsDefaultEnum())
                defaultEnumValue = enumValue;
            if (element.getEnumIndexType() == EnumIndexType.String)
                stringIndexEnumValue.put(enumValue.getStringIndex(), enumValue);
            else
                intIndexEnumValue.put(enumValue.getIndex(), enumValue);
        }

        if (value == null ||"".equals(value.toString().trim())) {
            return null;
        }

        String indexValue = null;
        GspEnumValue enumInfo = null;
        if (element.getEnumIndexType() == EnumIndexType.String) {
            String index = getStringValue(element, value);
            enumInfo = stringIndexEnumValue.get(index);
            indexValue = index;
        } else {
            int index = getIntValue(element, value);
            enumInfo = intIndexEnumValue.get(index);
            indexValue = String.valueOf(index);
        }

        //element.getContainEnumValues().get(index);
        if (enumInfo == null)
            throw new CefRepositoryException("字段" + element.getLabelID()
                    + "中没有索引值为" + indexValue + "的枚举项");
        return enumInfo.getValue();
    }

    private static Object getAssoValue(IGspCommonElement element, ICefReader reader) {
        if(element.getIsRefElement()){
            GspBusinessEntity gspBusinessEntity = CMEngineUtil.getMetadataContent(element.getParentAssociation().getRefModelID());
            GspBizEntityObject gspBizEntityObject = gspBusinessEntity.getNode(element.getParentAssociation().getRefObjectCode());
            for(IGspCommonField gspCommonField: gspBizEntityObject.getContainElements()){
                if(gspCommonField.getID().equals(element.getRefElementId())){
                    var association = gspCommonField.getChildAssociations().get(0);
                    if (association == null)
                        throw new CefRepositoryException("字段" + element.getLabelID() + "设置的关联是空");
                    AssociationInfo info = new AssociationInfo(association);
                    //递归拼字段
                    info.setValue(gspCommonField.getLabelID(), getStringValue(element, getStringValue(element, reader.readValue(element.getLabelID()))));
                    if (association.getRefElementCollection() == null || association.getRefElementCollection().size() < 1)
                        return info;

                    for (var refElement : association.getRefElementCollection()) {
                        if(refElement.getIsUdt()){
                            info.setValue(refElement.getLabelID(), ReadUtils.getUdtData(element, true, reader, (IGspCommonElement)refElement));
                        }
                        else {
                            info.setValue(refElement.getLabelID(),
                                    getValue(element.getLabelID(), (IGspCommonElement) refElement,
                                            reader));
                        }
                    }
                    return info;
                }
            }
        }
        var association = element.getChildAssociations().get(0);

        if (association == null)
            throw new CefRepositoryException("字段" + element.getLabelID() + "设置的关联是空");
        AssociationInfo info = new AssociationInfo(association);
        info.setValue(element.getLabelID(), getStringValue(element, getStringValue(element, reader.readValue(element.getLabelID()))));
        if (association.getRefElementCollection() == null || association.getRefElementCollection().size() < 1)
            return info;

        for (var refElement : association.getRefElementCollection()) {
            if(refElement.getIsUdt()){
                info.setValue(refElement.getLabelID(), ReadUtils.getUdtData(true, reader, (IGspCommonElement)refElement));
            }
            else {
                info.setValue(refElement.getLabelID(), getValue((IGspCommonElement) refElement, reader));
            }
        }

        //TODO 关联后续处理
        return info;
    }

    private static String getStringValue(IGspCommonElement element, Object value) {
        if (value == null)
            return EntityDataPropertyValueUtils.getStringPropertyDefaultValue();
        else  if(value.toString().toLowerCase().contains("oracle.sql.clob")){
            return getClobValue(element, value);
        }
        return value.toString();
    }

    private static Integer getIntValue(IGspCommonElement element, Object value) {
        if (value == null)
            return null;
        try {
            return Integer.valueOf(value.toString());
        }
        catch (Exception ex){
            throw new CefRepositoryException("字段["+element.getLabelID()+"]转换为Integer类型失败,值为["+value.toString()+"]");
        }
    }

    private static BigDecimal getDecimalValue(IGspCommonElement element, Object value) {
        if (value == null)
            return null;
        if(value instanceof BigDecimal)
            return (BigDecimal) value;
        try {
            return new BigDecimal(value.toString());
        }
        catch (Exception ex){
            throw new CefRepositoryException("字段["+element.getLabelID()+"]转换为BigDecimal类型失败,值为["+value.toString()+"]");
        }
    }

    private static Boolean getBoolValue(IGspCommonElement element, Object value) {
        if (value == null)
            return null;
        switch (value.toString().toLowerCase()) {
            case "0":
            case "false":
                return false;
            case "1":
            case "true":
                return true;
            default:{
                logger.error("字段[code=" + element.getLabelID() + ",elementid="+element.getID()+  " belogobjectid=" +element.getBelongObject().getID()+  " belongobjectname="  + element.getBelongObject().getName() + " beid=" + element.getBelongObject().getBelongModel().getID() + " ]转换为boolean类型失败,值为" + value);
                throw new CefRepositoryException("字段[code=" + element.getLabelID()  + " ]转换为boolean类型失败,值为" + value);
            }
        }
//        return getBoolean(value.toString());
    }

    private static SimpleDateFormat sdfForYYYYMMDD = new SimpleDateFormat("yyyyMMdd");
    private static Date getDateValue(IGspCommonElement element, Object value) {
        if (value == null)
            return EntityDataPropertyValueUtils.getDateTimePropertyDefaultValue();
        try {
            if(value instanceof String&&((String)value).length()==8) {
                return sdfForYYYYMMDD.parse((String) value);
            }
            return (Date) value;
        }
        catch (Exception ex){
            logger.error("字段[code=" + element.getLabelID() + ",elementid="+element.getID()+  " belogobjectid=" +element.getBelongObject().getID()+  " belongobjectname="  + element.getBelongObject().getName() + " beid=" + element.getBelongObject().getBelongModel().getID() + " ]转换为Date类型失败,值为["+value.toString()+"]");
            throw new CefRepositoryException("字段["+element.getLabelID()+"]转换为Date类型失败,值为["+value.toString()+"]");
        }
    }

    private static String getTextValue(IGspCommonElement element, Object value) {
        if (value == null)
            return EntityDataPropertyValueUtils.getStringPropertyDefaultValue();
        return getClobValue(value);
    }

    private static byte[] getBinaryValue(IGspCommonElement element, Object value) {
        if (value == null)
            return EntityDataPropertyValueUtils.getBinaryPropertyDefaultValue();
        //TODO: 这个地方为啥不走getDbProcessor().getBlobValue
        if(value instanceof byte[])
            return (byte[])value;
        return Convert.toUTF8(value.toString());

    }


    private static boolean getBoolean(String value) {
        switch (value) {
            case "0":
                return false;
            case "1":
                return true;
            default:
                throw new CefRepositoryException("不正确的布尔值：" + value);
        }
    }

    private static String getClobValue(Object obj) {
        if (obj instanceof String) {
            return obj.toString();
        } else {
            try {
                return obj == null ? EntityDataPropertyValueUtils.getStringPropertyDefaultValue() : ((Clob) obj).getSubString(1L, (int) ((Clob) obj).length());
            } catch (Exception e) {
                throw new CefRepositoryException("解析备注类型数据：" + obj + "失败");
            }
        }
    }

    private static String getClobValue(IGspCommonElement element, Object obj) {
        if (obj instanceof String) {
            return obj.toString();
        } else {
            try {
                return obj == null ? EntityDataPropertyValueUtils.getStringPropertyDefaultValue() : ((Clob) obj).getSubString(1L, (int) ((Clob) obj).length());
            } catch (Exception e) {
                throw new CefRepositoryException("字段["+element.getLabelID()+"]转换为Clob类型失败,值为" + obj);
            }
        }
    }

    //endregion

    //region udt字段賦值

    public static void setUdtData(ICefReader reader, IEntityData data, IGspCommonElement element) {
        Object value = getUdtData(reader, element);
        data.setValue(element.getLabelID(), value);
    }

    public static void setUdtData(ICefReader reader, boolean isAssoRef, IEntityData data, IGspCommonElement element) {
        Object value = getUdtData(isAssoRef, reader, element);
        data.setValue(element.getLabelID(), value);
    }

    public static Object getUdtData(ICefReader reader, IGspCommonElement element){
        return getUdtData(true, reader, element);
    }

    public static Object getUdtData(boolean isAssoRef, ICefReader reader, IGspCommonElement element){
        UnifiedDataTypeDef udt = CMEngineUtil.getMetadataContent(element.getUdtID());
        if (udt == null)
            throw new CefRepositoryException("没有找到业务字段["+element.getLabelID()+"]对应的UDT元数据：" + element.getUdtID());
        Object value = udt instanceof SimpleDataTypeDef ? setSimpleUdtData(null, reader, element,
                (SimpleDataTypeDef) udt) : setComplexUdtData( reader,isAssoRef, element, (ComplexDataTypeDef) udt);
        return value;
    }

    public static Object getUdtData(IGspCommonElement assoElment, boolean isAssoRef, ICefReader reader, IGspCommonElement element){
        UnifiedDataTypeDef udt = CMEngineUtil.getMetadataContent(element.getUdtID());
        if (udt == null)
            throw new CefRepositoryException("没有找到业务字段["+element.getLabelID()+"]对应的UDT元数据：" + element.getUdtID());
        Object value = udt instanceof SimpleDataTypeDef ? setSimpleUdtData(assoElment, reader, element,
                (SimpleDataTypeDef) udt) : setComplexUdtData( reader,isAssoRef, element, (ComplexDataTypeDef) udt);
        return value;
    }

    private static Object setSimpleUdtData(IGspCommonElement assoElement, ICefReader reader, IGspCommonElement element, SimpleDataTypeDef udt) {
        LinkedHashMap<String, String> map = new LinkedHashMap();
        switch (udt.getObjectType()) {
            case None:
            case Enum:{
                if(assoElement != null && assoElement.getIsRefElement() && assoElement.getParentAssociation() != null && assoElement.getParentAssociation().getBelongElement() != null){//处理关联-带出关联-带出UDT字段的场景
                    IGspCommonField parentElement = assoElement.getParentAssociation().getBelongElement();
                    map.put(udt.getCode(), parentElement.getLabelID() + "_" + element.getLabelID());
                }
                else {
                    map.put(udt.getCode(), element.getLabelID());
                }
                break;
            }
            case Association:
                addAssUdtMapping(map, element, udt);
                break;
            default:
                break;
        }

        return getUdtData(map, reader, udt);
    }

    private static void addAssUdtMapping(LinkedHashMap<String, String> mapping, IGspCommonElement element, SimpleDataTypeDef udt) {
        if(element.getChildAssociations() != null && element.getChildAssociations().size() > 0){
            var fieldAssociation = element.getChildAssociations().get(0);
            var udtAssociation = udt.getChildAssociations().get(0);
            mapping.put(udt.getCode(), element.getLabelID());
            for (var item : fieldAssociation.getRefElementCollection()) {
                if (!item.getIsFromAssoUdt()) {
                    continue;
                }
                IGspCommonField udtRefField =
                        getElementByRefElementID(udtAssociation, item.getRefElementId(), item.getLabelID(), udt);
                mapping.put(udtRefField.getLabelID(), item.getLabelID());
                //todo 暂时隐藏，跟其他模块一块提交
                if(udtRefField.getIsUdt()){
                    //判断是否多值UDT 或者继续？
                    UnifiedDataTypeDef glUdt = CMEngineUtil.getMetadataContent(udtRefField.getUdtID());
                    if(glUdt instanceof ComplexDataTypeDef){
                        ComplexDataTypeDef complexDataTypeDef = (ComplexDataTypeDef)glUdt;
                        if(complexDataTypeDef.getDbInfo().getMappingType() == ColumnMapType.MultiColumns){
                            for(IGspCommonField udtField : complexDataTypeDef.getContainElements()){
                                mapping.put(udtRefField.getLabelID() + "_" + udtField.getLabelID(), item.getLabelID() + "_" + udtField.getLabelID());
                            }
                        }
                    }
                }
            }
        }
        else {//关联udt
            mapping.put(udt.getCode(), element.getLabelID());
            for(IGspCommonField udtField: udt.getChildAssociations().get(0).getRefElementCollection()){
                mapping.put(udtField.getLabelID(), element.getLabelID() + "_" + udtField.getLabelID());
            }
        }
    }

    private static IGspCommonField getElementByRefElementID(
            GspAssociation udtAssociation,
            String refElementID,
            String labelId,
            SimpleDataTypeDef udt) {
        for (var item : udtAssociation.getRefElementCollection()) {
            if (refElementID.equals(item.getRefElementId())) {
                return item;
            }
        }
        throw new CefRepositoryException(
                "找不到UDT[" + udt.getCode() + "]关联字段" + refElementID + "----" + labelId);
    }

    private static Object setComplexUdtData(ICefReader reader, boolean isAssoRef, IGspCommonElement element, ComplexDataTypeDef udt) {
        LinkedHashMap<String, String> map = null;
        switch (udt.getDbInfo().getMappingType()) {
            case MultiColumns:
                map = getMultiColumnsUdtMap(reader, isAssoRef, element, udt);
                break;
            case SingleColumn:
                map = getSingleColumnUdtMap(reader, element, udt);
                break;
        }

        return getUdtData(map, reader, udt);
    }

    private static Object getUdtData(
            LinkedHashMap<String, String> map,
            ICefReader reader,
            UnifiedDataTypeDef udt) {
        CefMappingReader mappingReader = new CefMappingReader(map, reader);
        INestedRepository u1Repos = UdtManagerUtils.getUdtRepositoryFactory().createRepository(udt.getUdtType());
        return u1Repos.readData(mappingReader);
    }

    private static LinkedHashMap<String, String> getSingleColumnUdtMap(ICefReader reader, IGspCommonElement element, ComplexDataTypeDef udt) {
        LinkedHashMap<String, String> map = new LinkedHashMap<>();
        map.put(udt.getCode(), element.getLabelID());
        return map;
    }

    /**
     *
     * @param reader
     * @param isAssoRef 是否关联带出
     * @param element
     * @param udt
     * @return
     */
    private static LinkedHashMap<String, String> getMultiColumnsUdtMap(ICefReader reader, boolean isAssoRef, IGspCommonElement element, ComplexDataTypeDef udt) {
        LinkedHashMap<String, String> map = new LinkedHashMap<>();
        for (IGspCommonField childElement : element.getChildElements()) {
            String udtElementId = element.getMappingRelation().getMappingInfo(childElement.getID());
            IGspCommonField udtElement = udt.findElement(udtElementId);
            //labeldId与Code不一致，说明是带出的 或者应该用ContainElements来判断
            //这个判断不准确
            if(isAssoRef){
                //储粮存在手动修改 labelid和code不一致的情况（非带出多值UDT）
                if(childElement.getLabelID().equalsIgnoreCase(childElement.getCode())){
                    map.put(udtElement.getLabelID(), childElement.getLabelID());
                }
                else {
                    map.put(udtElement.getLabelID(), childElement.getLabelID() + "_" + childElement.getCode());
                }
            }
            else {
                map.put(udtElement.getLabelID(), childElement.getLabelID());
            }

        }
        return map;
    }

    //endregion

    //region udt字段新增
    public static Object getUdtValue(GspBizEntityObject beObject, String labelId, String belongEleLabelId, Object udtData){

        var elementDic = beObject.getAllElementDic();
        if(!elementDic.containsKey(belongEleLabelId))
            return null;
        var parentElement = elementDic.get(belongEleLabelId);
//        var udtData = entityData.getValue(belongEleLabelId);
        UnifiedDataTypeDef udt = CMEngineUtil.getMetadataContent(parentElement.getUdtID());
        if (udt == null)
            throw new CefRepositoryException("没有找到业务字段元数据：" + parentElement.getUdtID());

        if (udt instanceof SimpleDataTypeDef)
            return getUdtChangeValue(udtData, udt.getUdtType(), udt.getCode());
        else{
            ComplexDataTypeDef complexUdt = (ComplexDataTypeDef)udt;
            switch (complexUdt.getDbInfo().getMappingType()) {
                case MultiColumns:
                    IGspCommonField udtElement =getUdtElement(parentElement, complexUdt, labelId);
                    return getUdtChangeValue(udtData, udt.getUdtType(), udtElement.getLabelID());
                case SingleColumn:
                    return getUdtChangeValue(udtData, udt.getUdtType(), udt.getCode());
            }
        }
        return null;
    }

    public static IGspCommonField getUdtElement(IGspCommonField element, ComplexDataTypeDef udt,String columnName){
        IGspCommonField childElement = findChildElementByLabelId(element, columnName);
        String udtElementId = element.getMappingRelation().getMappingInfo(childElement.getID());
        IGspCommonField udtElement = udt.findElement(udtElementId);
        if (udtElement == null)
            throw new CefRepositoryException("udt[" + udt.getCode() + "]中为找到id为" + udtElementId + "的字段");

        return udtElement;
    }

    private static IGspCommonField findChildElementByLabelId(IGspCommonField element, String lableId) {
        for (IGspCommonField childElement :
                element.getChildElements()) {
            if (childElement.getLabelID().equals(lableId))
                return childElement;
        }
        throw new CefRepositoryException("未找到字段" + lableId);
    }

    public static Object getUdtChangeValue(Object propertyValue, String configId, String propName) {
        if (propertyValue instanceof ValueObjModifyChangeDetail) {
            propertyValue = ((ValueObjModifyChangeDetail) propertyValue).getData();
        }
        if (!(propertyValue instanceof ICefData)) {
            return propertyValue;
        }
        return UdtManagerUtils.getUdtRepositoryFactory().createRepository(configId).getPersistenceValue(propName, (ICefData) propertyValue);

    }
    //endregion

    //region udt字段更新

//    public static Object getUdtModifyValue(GspBizEntityObject beObject, String labelId, String belongEleLabelId, Object udtChange){
//
//        var elementDic = beObject.getAllElementDic();
//        if(!elementDic.containsKey(belongEleLabelId))
//            return null;
//        var parentElement = elementDic.get(belongEleLabelId);
////        var udtData = entityData.getValue(belongEleLabelId);
//
//        GspMetadata udtMetadata = getCustomizationRtService().getMetadata(parentElement.getUdtID());
//        if (udtMetadata == null)
//            throw new RuntimeException("没有找到业务字段元数据：" + parentElement.getUdtID());
//        UnifiedDataTypeDef udt = (UnifiedDataTypeDef) udtMetadata.getContent();
//
//        if (udt instanceof SimpleDataTypeDef)
//            return getUdtChangeValue(udtData, udt.getUdtType(), udt.getCode());
//        else{
//            ComplexDataTypeDef complexUdt = (ComplexDataTypeDef)udt;
//            switch (complexUdt.getDbInfo().getMappingType()) {
//                case MultiColumns:
//                    IGspCommonField udtElement =getUdtElement(parentElement, complexUdt, labelId);
//                    return getUdtChangeValue(udtData, udt.getUdtType(), udtElement.getLabelID());
//                case SingleColumn:
//                    return getUdtChangeValue(udtData, udt.getUdtType(), udt.getCode());
//            }
//        }
//        return null;
//    }


    //endregion

}
